# coding: utf-8
from api.proxy_client import api_key_decryption
from api.proxy_bitmex.bitmex import bitmex
import asyncio
import queue
import time
from PyQt5 import QtCore
import traceback
import threading
import simplejson
from utils import log_file


def get_rest_client(api_key=None, api_secret=None, test=True):
    api_key, api_secret = api_key_decryption(api_key, api_secret)
    return bitmex(test=test, api_key=api_key, api_secret=api_secret)



class AsyncBitMexRest(threading.Thread):
    def __init__(self, client):
        super(AsyncBitMexRest, self).__init__()
        self.dict_msg = {}
        self.client = client
        self.keep_run = True
        self.q = queue.Queue()
        self.async_loop = asyncio.new_event_loop()
        self.ratelimit_limit = None
        self.ratelimit_remaining = None
        self.ratelimit_reset =None
        self.reversed_limit = 30

    @staticmethod
    def start_loop(loop):
        asyncio.set_event_loop(loop)
        loop.run_forever()

    def request(self, func, callback, *args, **kwargs):
        can_request = self.__check_bitmex_limit()
        if can_request:
            self.q.put((func, callback, args, kwargs))
        else:
            callback({'status_code': 403, 'reason': '服务器请求数限制，请稍后再重试', "data": []})

    def run(self):
        try:
            t = threading.Thread(target=self.start_loop, args=(self.async_loop,))
            t.start()
            while self.keep_run:
                func, callback, args, kwargs = self.q.get(block=True)

                async def async_req(func, callback, *args, **kwargs):
                    try:
                        ret = func(*args, **kwargs).result()
                        status_code = ret[1].status_code
                        reason = ret[1].reason
                        headers = ret[1].headers
                        self.ratelimit_limit = headers["X-RateLimit-Limit"]
                        self.ratelimit_remaining = headers["X-RateLimit-Remaining"]
                        self.ratelimit_reset = headers["X-RateLimit-Reset"]
                        if status_code == 200:
                            callback({'status_code': status_code, 'data': ret[0], "reason": ""})
                        elif status_code == 503:
                            callback({'status_code': status_code,
                                      'data': ret[0],
                                      "reason": "BitMex 交易系统繁忙，请至少等0.5秒后重试"})
                            log_file(f"BitMex 交易系统繁忙，请至少等0.5秒后重试: {reason}")
                        else:
                            callback({'status_code': status_code, 'reason': reason, "data": []})

                    except Exception as e:
                        callback({'status_code': 500, 'reason': str(e), "data": []})
                        import traceback
                        log_file(traceback.format_exc())
                asyncio.run_coroutine_threadsafe(async_req(func, callback, *args, **kwargs), self.async_loop)
        except:
            import traceback
            traceback.print_exc()

    def __check_bitmex_limit(self):
        """
        检测当前一段时间后rest请求是否达到最大连接数
        :return:
        """
        if self.ratelimit_limit and self.ratelimit_remaining and self.ratelimit_reset:
            if float(self.ratelimit_remaining) - self.reversed_limit <= 0:
                wait_time = float(self.ratelimit_reset) - time.time()
                if wait_time > 0:
                    return False
        return True

    def close(self):
        self.keep_run = False


if __name__ == "__main__":
    from api.bitmex_rest import get_rest_client
    from api.proxy_client import ProxyClient
    from api.bitmex_rest import AsyncBitMexRest

    ProxyClient()
    client = get_rest_client(api_key="Uvk1Q3VABKrKDy-uR0kSgVyD",
                             api_secret="nG4S3VhfH9CGq8BjGMxsJeXulMTLzot8zYuywoBC6L114zSw")

    async_client = AsyncBitMexRest(client)
    async_client.start()

    def test_async(ret):
        print(ret)

    # for i in range(1, 300):
    #     async_client.request(client.Order.Order_getOrders, test_async, symbol="XBTUSD")
    #     time.sleep(0.5)
    # time.sleep(5)

    async_client.request(client.Position.Position_get, test_async)
